/* eslint-disable import/no-extraneous-dependencies */
/**
 * @fileoverview the utils function for custom render
 * @author houquan | houquan@bytedance.com
 * @version 1.0.0 | 2020-03-16 | houquan      // initial version
 */

import fs from "node:fs";
import path from "node:path";
import findUp from "find-up";
import JSON5 from "json5";
import less from "less";
import tsModule from "typescript";

// import LessPluginAlias from "./ts-less-plugin-alias.js";

export interface Logger {
  log: (msg: string) => void;
  error: (error: Error) => void;
}

export interface InjectOptionsFromTSPlugin {
  fileName: string;
  compilerOptions: tsModule.CompilerOptions;
  logger: Logger;
}

const checkExtList = [".less", ".css"];

export function normalizePath(filename: string) {
  if (/\.(?:less|css)$/i.test(filename)) {
    return fs.existsSync(filename) ? filename : undefined;
  }

  for (let i = 0, len = checkExtList.length; i < len; i++) {
    const ext = checkExtList[i];
    if (fs.existsSync(`${filename}${ext}`)) {
      return `${filename}${ext}`;
    }
  }
}

export function transformPathsToAlias({
  paths,
  baseUrl,
}: tsModule.CompilerOptions = {}): Record<string, string[]> {
  const alias: Record<string, string[]> = {};
  if (paths && baseUrl) {
    for (let key of Object.keys(paths)) {
      const pathList = paths[key];
      if (key.endsWith("/*")) {
        key = key.slice(0, -2);
      }
      alias[key] = pathList.map((p) =>
        path.resolve(baseUrl, p.endsWith("/*") ? p.slice(0, -2) : p),
      );
    }
  }

  return alias;
}

export function readTSConf() {
  const tsconfPath = findUp.sync("tsconfig.json");

  if (!tsconfPath) {
    return {};
  }
  const tsconfigJSON =
    JSON5.parse(fs.readFileSync(tsconfPath).toString()) || {};
  return tsconfigJSON.compilerOptions || {};
}

export function createCustomRender(
  options: Less.Options & { prefix?: string },
) {
  const { prefix = "~", ...lessOpts } = options;
  return async function (
    css: string,
    {
      fileName: filename,
      logger,
      compilerOptions = readTSConf(),
    }: InjectOptionsFromTSPlugin,
  ): Promise<string> {
    let transformedCSS = "";

    const alias = transformPathsToAlias(compilerOptions);
    less.render(
      css,
      {
        syncImport: true,
        filename,
        plugins: [
          new (await import("./ts-less-plugin-alias.js")).default({
            prefix,
            alias,
            logger,
          }),
        ],
        ...lessOpts,
      },
      (error, output) => {
        if (error || output === undefined) {
          throw error;
        }
        transformedCSS = output.css.toString();
      },
    );

    return transformedCSS;
  };
}
